/**
 *  Copyright (C) 2008-2013  Telosys project org. ( http://www.telosys.org/ )
 *
 *  Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *          http://www.gnu.org/licenses/lgpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.telosys.common.xml;

import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.telosys.common.TelosysRuntimeException;
import org.telosys.util.Conv;
import org.telosys.util.DateUtil;
import org.telosys.util.XmlUtil;

/**
 * Generic XML Wrapper ancestor
 * 
 * @author Laurent GUERIN
 *  
 */
public abstract class XmlWrapperAncestor
{
    //-----------------------------------------------------------------------------------------------
    // CONST
    //-----------------------------------------------------------------------------------------------
    public final static int DATE_ONLY = 1 ;
    public final static int TIME_ONLY = 2 ;
    public final static int DATE_AND_TIME = 3 ;
    
//--- Static references to "SimpleDateFormat" removed in ver 1.0.3
//  N.B. the setLenient(false) cannot be done in static block    
//    private final static SimpleDateFormat dateFormatISO   = new SimpleDateFormat("yyyy-MM-dd");
////    {
////        dateFormatISO.setLenient(false); // Parsing non lenient ( exception if invalid date )
////    }
//
//    private final static SimpleDateFormat datetimeFormatISO   = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
////    {
////        datetimeFormatISO.setLenient(false); // Parsing non lenient ( exception if invalid datetime )
////    }
//
//    private final static SimpleDateFormat timeFormatISO   = new SimpleDateFormat("HH:mm:ss");
////    {
////        timeFormatISO.setLenient(false); // Parsing non lenient ( exception if invalid datetime )
////    }

    //-----------------------------------------------------------------------------------------------
    // PRIVATE ATTRIBUTES
    //-----------------------------------------------------------------------------------------------
    private Class<?> _beanClass = null ;

    //-----------------------------------------------------------------------------------------------
    // CONSTRUCTOR(S)
    //-----------------------------------------------------------------------------------------------
    /**
     * Constructor
     * @param beanClass the class of the bean managed by the wrapper
     */
    protected XmlWrapperAncestor(Class<?> beanClass)
    {
        if (beanClass == null)
        {
            throwException("constructor : bean class is null");
        }
        _beanClass = beanClass;
    }

    //-----------------------------------------------------------------------------------------------
    // METHODS
    //-----------------------------------------------------------------------------------------------
    /**
     * Returns true if the given value is null or void
     * @param s
     * @return
     */
    protected boolean isNullOrVoid(String s)
    {
        if ( s == null ) return true ;
        if ( s.trim().length() == 0 ) return true ;
        return false ;
    }
        
    //-----------------------------------------------------------------------------------
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString()
    {
        return "XML mapper '" + this.getClass().getName() + "' for '" + getBeanClassName() + "' objects";
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns the class of the bean managed by the wrapper
     * @return 
     */
    public final Class<?> getBeanClass()
    {
        return _beanClass ;
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns the class name of the bean managed by the wrapper
     * @return
     */
    public final String getBeanClassName()
    {
        return ( _beanClass != null ? _beanClass.getName() : "???" ) ;
    }

    //-----------------------------------------------------------------------------------
    /**
     * Throws a TelosysRuntimeException with the given message
     * 
     * @param sMessage
     *            the error message
     */
    protected final void throwException(String sMessage)
    {
        throw new TelosysRuntimeException("XML error in '" + this.getClass().getName() +"' : " + sMessage);
    }

    //-----------------------------------------------------------------------------------
    /**
     * Return a boolean value from the given string attribute value
     * 
     * @param sFieldName  the name of the field
     * @param sValue      the attribute value
     * @return true if the given value is "1", else false
     * @throws TelosysRuntimeException
     *             if the given value is not valid
     */
    protected boolean getBoolean(String sFieldName, String sValue)
    {
        if (sValue != null)
        {
            return sValue.equals("1");
        }
        else
        {
            throwException("field '" + sFieldName + "' (boolean) : value is null ");
        }
        return false;
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns a long value from the given string attribute value 
     * @param sFieldName  the name of the field
     * @param sValue      the value to convert ( cannot be null or void )
     * @return the long value
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     */
    protected long getLong(String sFieldName, String sValue)
    {
        if (sValue != null)
        {
            try
            {
                return Long.parseLong(sValue);
            } catch (NumberFormatException ex)
            {
                throwException("field '" + sFieldName + "' (integer) : invalid value '" + sValue + "' (NumberFormatException) ");
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (integer) : value is null ");
        }
        return 0;
    }

    /**
     * Returns a long value from the given string attribute value 
     * @param sFieldName     the name of the field
     * @param sValue         the value to convert ( can be null or void )
     * @param lDefaultValue  the default value to use if the string is null or void 
     * @return the long value
     * @throws TelosysRuntimeException  if the given value is invalid
     */
    protected long getLong(String sFieldName, String sValue, long lDefaultValue )
    {
        if (sValue != null)
        {
            if ( sValue.trim().length() > 0 ) // Not Void
            {
                return getLong(sFieldName, sValue);
            }
            else // Void
            {
                return lDefaultValue ;
            }
        }
        else // Null
        {
            return lDefaultValue ;
        }
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns an integer value from the given string attribute value 
     * @param sFieldName   the name of the field
     * @param sValue      the value to convert ( cannot be null or void )
     * @return the integer value
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     */
    protected int getInt(String sFieldName, String sValue)
    {
        return Conv.toInt(getLong(sFieldName, sValue), Conv.STRICT );
    }

    /**
     * Returns an integer value from the given string attribute value 
     * @param sFieldName     the name of the field
     * @param sValue         the value to convert ( can be null or void )
     * @param iDefaultValue  the default value to use if the string is null or void 
     * @return the integer value
     * @throws TelosysRuntimeException   if the given value is invalid
     */
    protected int getInt(String sFieldName, String sValue, int iDefaultValue )
    {
        return Conv.toInt(getLong(sFieldName, sValue, iDefaultValue), Conv.STRICT);
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns a short value from the given string attribute value 
     * @param sFieldName the name of the field
     * @param sValue     the value to convert ( cannot be null or void )
     * @return 
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     */
    protected short getShort(String sFieldName, String sValue)
    {
        return Conv.toShort(getLong(sFieldName, sValue), Conv.STRICT );
    }

    /**
     * Returns a short value from the given string attribute value 
     * @param sFieldName the name of the field
     * @param sValue     the value to convert ( can be null or void )
     * @param defaultValue  the default value to use if the string is null or void 
     * @return 
     * @throws TelosysRuntimeException if the given value is invalid
     */
    protected short getShort(String sFieldName, String sValue, short defaultValue )
    {
        return Conv.toShort(getLong(sFieldName, sValue, defaultValue), Conv.STRICT);
    }

    //-----------------------------------------------------------------------------------
    
    /**
     * Returns a char value from the given string attribute value 
     * @param sFieldName  the name of the field
     * @param sValue      the value to convert ( cannot be null or void )
     * @return 
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     * @since v 1.0.2
     */
    protected char getChar(String sFieldName, String sValue)
    {
        return Conv.toChar(getLong(sFieldName, sValue), Conv.STRICT );
    }
    
    /**
     * Returns a char value from the given string attribute value 
     * @param sFieldName the name of the field
     * @param sValue     the value to convert ( can be null or void )
     * @param defaultValue  the default value to use if the string is null or void 
     * @return 
     * @throws TelosysRuntimeException if the given value is invalid
     * @since v 1.0.2
     */
    protected char getChar(String sFieldName, String sValue, char defaultValue)
    {
        return Conv.toChar(getLong(sFieldName, sValue, defaultValue), Conv.STRICT );
    }
    
    //-----------------------------------------------------------------------------------
    /**
     * Returns a byte value from the given string attribute value 
     * @param sFieldName the name of the field
     * @param sValue     the value to convert ( cannot be null or void )
     * @return the byte value
     * @throws TelosysRuntimeException  if the given value is null, void or invalid
     */
    protected byte getByte(String sFieldName, String sValue)
    {
        return Conv.toByte(getLong(sFieldName, sValue), Conv.STRICT );
    }

    /**
     * Returns a byte value from the given string attribute value 
     * @param sFieldName    the name of the field
     * @param sValue        the value to convert ( can be null or void )
     * @param defaultValue  the default value to use if the string is null or void 
     * @return the byte value
     * @throws TelosysRuntimeException if the given value is invalid
     */
    protected byte getByte(String sFieldName, String sValue, short defaultValue )
    {
        return Conv.toByte(getLong(sFieldName, sValue, defaultValue), Conv.STRICT);
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns a double value from the given string attribute value
     * @param sFieldName  the name of the field
     * @param sValue      the value to convert ( cannot be null or void )
     * @return the double value
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     */
    protected double getDouble(String sFieldName, String sValue)
    {
        if (sValue != null)
        {
            try
            {
                return Double.parseDouble(sValue);
            } catch (NumberFormatException ex)
            {
                throwException("field '" + sFieldName + "' (double) : invalid  value '" + sValue + "' (NumberFormatException) ");
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (double) : value is null ");
        }
        return 0;
    }

    /**
     * Returns a double value from the given string attribute value
     * @param sFieldName   the name of the field
     * @param sValue       the value to convert ( can be null or void )
     * @param defaultValue the default value to use if the string is null or void 
     * @return the double value
     * @throws TelosysRuntimeException if the given value is not valid
     */
    protected double getDouble(String sFieldName, String sValue, double defaultValue)
    {
        if (sValue != null)
        {
            if ( sValue.trim().length() > 0 ) // Not Void
            {
                return getDouble(sFieldName, sValue);
            }
            else // Void
            {
                return defaultValue ;
            }
        }
        else // Null
        {
            return defaultValue ;
        }
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns a float value from the given string attribute value
     * 
     * @param sFieldName the name of the field
     * @param sValue     the value to convert ( cannot be null or void )
     * @return the float value
     * @throws TelosysRuntimeException if the given value is null, void or invalid
     */
    protected float getFloat(String sFieldName, String sValue)
    {
        if (sValue != null)
        {
            try
            {
                return Float.parseFloat(sValue);
            } catch (NumberFormatException ex)
            {
                throwException("field '" + sFieldName + "' (float) : invalid  value '" + sValue + "' (NumberFormatException) ");
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (float) : value is null ");
        }
        return 0;
    }

    /**
     * Returns a float value from the given string attribute value
     * @param sFieldName    the name of the field
     * @param sValue        the value to convert ( can be null or void )
     * @param defaultValue  the default value to use if the string is null or void 
     * @return the float value
     * @throws TelosysRuntimeException if the given value is not valid
     */
    protected float getFloat(String sFieldName, String sValue, float defaultValue)
    {
        if (sValue != null)
        {
            if ( sValue.trim().length() > 0 ) // Not Void
            {
                return getFloat(sFieldName, sValue);
            }
            else // Void
            {
                return defaultValue ;
            }
        }
        else // Null
        {
            return defaultValue ;
        }
    }

    //-----------------------------------------------------------------------------------
    /**
     * Parse the given date ( supposed to be not null and in ISO format : "YYYY-MM-DD" )
     * @param sFieldName
     * @param sDate
     * @return
     * @since 1.0.2
     */
    private java.util.Date parseDate( String sFieldName, String sDate )
    {
//        char c = 0 ;
//        for ( int i = 0 ; i < 10 ; i++ ) // the length is 10 "YYYY-MM-DD"
//        {
//            c = sDate.charAt(i);
//            if ( ( c < '0' || c > '9') && ( c != '-' ) )  
//            {
//                throwException("field '" + sFieldName + "' (date) : invalid value '" + sDate + "' ( invalid char '" + c + "' ) ");
//            }
//            if ( c == '-' && ( i != 4 && i != 7 ) )
//            {
//                throwException("field '" + sFieldName + "' (date) : invalid value '" + sDate + "' ( '-' at position " + i + ") ");                            
//            }
//        }
//        
//        java.util.Date ret = null ;
//        try
//        {
//            //--- Try to parse the input date ( with non lenient parsing => check validity )
//            ret = dateFormatISO.parse(sDate);
//        } catch (ParseException e)
//        {
//            throwException("field '" + sFieldName + "' (date) : invalid value '" + sDate + "' ");
//        }
//        return ret ;
//
        java.util.Date ret = null ;
        try
        {
            //--- Try to parse the input date ( with non lenient parsing => check validity )
            ret = DateUtil.parseDate(sDate);
        } catch (TelosysRuntimeException e)
        {
            throwException("field '" + sFieldName + "' : invalid date value '" + sDate + "' ");
        }
        return ret ;
    }
    
    /**
     * Parse the given time ( supposed to be not null and in ISO format : "HH:MM:SS" )
     * @param sFieldName
     * @param sTime
     * @return
     * @since 1.0.2
     */
    private java.util.Date parseTime( String sFieldName, String sTime )
    {
//        char c = 0 ;
//        for ( int i = 0 ; i < 8 ; i++ ) // the length is 8 "HH:MM:SS"
//        {
//            c = sTime.charAt(i);
//            if ( ( c < '0' || c > '9') && ( c != ':' ) )  
//            {
//                throwException("field '" + sFieldName + "' (time) : invalid value '" + sTime + "' ( invalid char '" + c + "' ) ");
//            }
//            if ( c == ':' && ( i != 2 && i != 5 ) )
//            {
//                throwException("field '" + sFieldName + "' (time) : invalid value '" + sTime + "' ( ':' at position " + i + ") ");                            
//            }
//        }
//
//        java.util.Date ret = null ;
//        try
//        {
//            //--- Try to parse the input date ( with non lenient parsing => check validity )
//            ret = timeFormatISO.parse(sTime);
//        } catch (ParseException e)
//        {
//            throwException("field '" + sFieldName + "' (time) : invalid value '" + sTime + "' ");
//        }
//        return ret ;
        
        java.util.Date ret = null ;
        try
        {
            //--- Try to parse the input date ( with non lenient parsing => check validity )
            ret = DateUtil.parseTime(sTime);
        } catch (TelosysRuntimeException e)
        {
            throwException("field '" + sFieldName + "' : invalid time value '" + sTime + "' ");
        }
        return ret ;
    }

    /**
     * Parse the given date & time ( supposed to be not null and in ISO format : "YYYY-MM-DD HH:MM:SS" )
     * @param sFieldName
     * @param sDateTime
     * @return
     * @since 1.0.2
     */
    private java.util.Date parseDateTime( String sFieldName, String sDateTime )
    {
//        char c = 0 ;
//        for ( int i = 0 ; i < 19 ; i++ ) // the length is 19 "YYYY-MM-DD HH:MM:SS"
//        {
//            c = sDateTime.charAt(i);
//            if ( ( c < '0' || c > '9') && ( c != '-' && c != ':' && c != ' ' ) )  
//            {
//                throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTime + "' ( invalid char '" + c + "' at position " + i + " ) ");
//            }
//            if ( c == '-' && ( i != 4 && i != 7 ) )
//            {
//                throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTime + "' ( '-' at position " + i + ") ");                            
//            }
//            if ( c == ' ' && ( i != 10 ) ) 
//            {
//                throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTime + "' ( ' ' at position " + i + ") ");                            
//            }
//            if ( c == ':' && ( i != 13 && i != 16 ) )
//            {
//                throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTime + "' ( ':' at position " + i + ") ");                            
//            }
//        }
//
//        java.util.Date ret = null ;
//        try
//        {
//            //--- Try to parse the input datetime ( with non lenient parsing => check validity )
//            ret = datetimeFormatISO.parse(sDateTime);
//        } catch (ParseException e)
//        {
//            throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTime + "' ");
//        }
//        return ret ;

        java.util.Date ret = null ;
        try
        {
            //--- Try to parse the input date ( with non lenient parsing => check validity )
            ret = DateUtil.parseDateTime(sDateTime);
        } catch (TelosysRuntimeException e)
        {
            throwException("field '" + sFieldName + "' : invalid datetime value '" + sDateTime + "' ");
        }
        return ret ;
    }
    
    //-----------------------------------------------------------------------------------
    /**
     * Returns a java.util.Date instance from the given date in ISO format "YYYY-MM-DD"<br>
     * ( old fashion function, kept for backward compatibility )
     * 
     * @param sFieldName  the name of the field
     * @param sDate       the attribute date value in ISO format ( "YYYY-MM-DD" )
     * @return the date instance ( or null is the input date is void )
     * @throws TelosysRuntimeException  if the given date is not valid
     */
    protected java.util.Date getDateISO(String sFieldName, String sDate) // "YYYY-MM-DD"
    {
        if (sDate != null)
        {
            String sDateTrim = sDate.trim();
            if (sDateTrim.equals(""))
            {
                return null;
            }
            else
            {
                if (sDateTrim.length() != 10)
                {
                    throwException("field '" + sFieldName + "' (date) : invalid length '" + sDateTrim + "' (length != 10) ");
                }
                else
                {
                    return parseDate(sFieldName,sDateTrim); // v 1.0.2
//                    char c = 0 ;
//                    for ( int i = 0 ; i < 10 ; i++ )
//                    {
//                        c = sDateTrim.charAt(i);
//                        if ( ( c < '0' || c > '9') && ( c != '-' ) )  
//                        {
//                            throwException("field '" + sFieldName + "' (date) : invalid value '" + sDateTrim + "' ( invalid char '" + c + "' ) ");
//                        }
//                        if ( c == '-' && ( i != 4 && i != 7 ) )
//                        {
//                            throwException("field '" + sFieldName + "' (date) : invalid value '" + sDateTrim + "' ( '-' at position " + i + ") ");                            
//                        }
//                    }
//                    try
//                    {
//                        //--- Try to parse the input date ( with non lenient parsing => check validity )
//                        return dateFormatISO.parse(sDateTrim);
//                    } catch (ParseException e)
//                    {
//                        throwException("field '" + sFieldName + "' (date) : invalid value '" + sDateTrim + "' ");
//                    }
                }
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (date) : value is null ");
        }
        return null;
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns a date instance containing the time from the given date in ISO format "HH:mm:ss"<br>
     * ( old fashion function, kept for backward compatibility )
     * 
     * @param sFieldName  the name of the field
     * @param sValue      the attribute time value in ISO format ( "HH:mm:ss" )
     * @return the date instance ( or null is the input date is void )
     * @throws TelosysRuntimeException  if the given date is not valid
     */
    protected java.util.Date getTimeISO(String sFieldName, String sValue) 
    {
        if (sValue != null)
        {
            String sTrim = sValue.trim();
            if (sTrim.equals(""))
            {
                return null;
            }
            else
            {
                if (sTrim.length() != 8)
                {
                    throwException("field '" + sFieldName + "' (time) : invalid length '" + sTrim + "' (length != 8) ");
                }
                else
                {
                    return parseTime(sFieldName, sTrim); // v 1.0.2
//                    char c = 0 ;
//                    for ( int i = 0 ; i < 8 ; i++ )
//                    {
//                        c = sTrim.charAt(i);
//                        if ( ( c < '0' || c > '9') && ( c != ':' ) )  
//                        {
//                            throwException("field '" + sFieldName + "' (time) : invalid value '" + sTrim + "' ( invalid char '" + c + "' ) ");
//                        }
//                        if ( c == ':' && ( i != 2 && i != 5 ) )
//                        {
//                            throwException("field '" + sFieldName + "' (time) : invalid value '" + sTrim + "' ( ':' at position " + i + ") ");                            
//                        }
//                    }
//                    try
//                    {
//                        //--- Try to parse the input date ( with non lenient parsing => check validity )
//                        return timeFormatISO.parse(sTrim);
//                    } catch (ParseException e)
//                    {
//                        throwException("field '" + sFieldName + "' (time) : invalid value '" + sTrim + "' ");
//                    }
                }
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (time) : value is null ");
        }
        return null;
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns a java.util.Date instance from the given date-time in ISO format "YYYY-MM-DD HH:MM:SS"<br>
     * ( old fashion function, kept for backward compatibility )
     * 
     * @param sFieldName  the name of the field
     * @param sDate       the attribute date-time value in ISO format ( "YYYY-MM-DD HH:MM:SS" )
     * @return the date instance ( or null is the input date is void )
     * @throws TelosysRuntimeException  if the given date is not valid
     */
    protected java.util.Date getDateTimeISO(String sFieldName, String sDate) // "YYYY-MM-DD HH:MM:SS"
    {
        if (sDate != null)
        {
            String sDateTrim = sDate.trim();
            if (sDateTrim.equals(""))
            {
                return null;
            }
            else
            {
                if (sDateTrim.length() != 19)
                {
                    throwException("field '" + sFieldName + "' (datetime) : invalid length '" + sDateTrim + "' (length != 19) ");
                }
                else
                {
                    return parseDateTime(sFieldName, sDateTrim); // v 1.0.2
//                    char c = 0 ;
//                    for ( int i = 0 ; i < 19 ; i++ )
//                    {
//                        c = sDateTrim.charAt(i);
//                        if ( ( c < '0' || c > '9') && ( c != '-' && c != ':' && c != ' ' ) )  
//                        {
//                            throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTrim + "' ( invalid char '" + c + "' at position " + i + " ) ");
//                        }
//                        if ( c == '-' && ( i != 4 && i != 7 ) )
//                        {
//                            throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTrim + "' ( '-' at position " + i + ") ");                            
//                        }
//                        if ( c == ' ' && ( i != 10 ) ) 
//                        {
//                            throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTrim + "' ( ' ' at position " + i + ") ");                            
//                        }
//                        if ( c == ':' && ( i != 13 && i != 16 ) )
//                        {
//                            throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTrim + "' ( ':' at position " + i + ") ");                            
//                        }
//                    }
//                    try
//                    {
//                        //--- Try to parse the input datetime ( with non lenient parsing => check validity )
//                        return datetimeFormatISO.parse(sDateTrim);
//                    } catch (ParseException e)
//                    {
//                        throwException("field '" + sFieldName + "' (datetime) : invalid value '" + sDateTrim + "' ");
//                    }
                }
            }
        }
        else
        {
            throwException("field '" + sFieldName + "' (datetime) : value is null ");
        }
        return null;
    }
    
    /**
     * Returns a java.util.Date instance for the given value, it can be <br>
     * . a date in ISO format "YYYY-MM-DD" <br>
     * . a time in ISO format "HH:MM:SS" <br>
     * . a date & time in ISO format "YYYY-MM-DD HH:MM:SS" <br>
     * 
     * @param sFieldName
     * @param sValue
     * @return the instance or null if the given value is void 
     * @since 1.0.2
     */
    protected java.util.Date getUtilDate(String sFieldName, String sValue)
    {
        if (sValue != null)
        {
            String sTrim = sValue.trim();
            //--- Use the LENGTH to determine the type 
            int length = sTrim.length() ;
            if ( length == 0 ) // ""
            {
                return null ;
            }
            else if ( length == 10 ) // "YYYY-MM-DD"
            {
                return parseDate(sFieldName, sTrim);
            }
            else if ( length == 8 ) // "HH:MM:SS"
            {
                return parseTime( sFieldName, sTrim );
            }
            else if ( length == 19 ) // "YYYY-MM-DD HH:MM:SS"
            {
                return parseDateTime( sFieldName, sTrim );
            }
            else
            {
                throwException("field '" + sFieldName + "' (util.Date) : invalid value '" + sTrim + "' ");
            }
        }
        return null;
    }
    
    /**
     * Returns a java.sql.Date instance for the given date in ISO format "YYYY-MM-DD" 
     * @param sFieldName
     * @param sValue
     * @return the instance or null if the given value is void 
     * @since 1.0.2
     */
    protected java.sql.Date getSqlDate ( String sFieldName, String sValue )
    {
        if ( sValue != null )
        {
            String sDateTrim = sValue.trim();
            //--- Use the LENGTH to determine the type 
            int length = sDateTrim.length() ;
            if ( length == 0 ) // ""
            {
                return null ;
            }
            else if ( length == 10 ) // "YYYY-MM-DD"
            {
                // NB : do not use 'java.sql.Date.valueOf(s)' because the validity is not checked !
                java.util.Date ud = parseDate( sFieldName, sDateTrim );
                return new java.sql.Date( ud.getTime() );
            }
            else
            {
                throwException("field '" + sFieldName + "' (sql.Date) : invalid value '" + sDateTrim + "' ");
            }
        }
        return null;
    }
    
    /**
     * Returns a java.sql.Time instance for the given time in ISO format "HH:MM:SS" 
     * @param sFieldName
     * @param sValue
     * @return the instance or null if the given value is void 
     * @since 1.0.2
     */
    protected java.sql.Time getSqlTime ( String sFieldName, String sValue )
    {
        if ( sValue != null )
        {
            String sTrim = sValue.trim();
            //--- Use the LENGTH to determine the type 
            int length = sTrim.length() ;
            if ( length == 0 ) // ""
            {
                return null ;
            }
            else if ( length == 8 ) // "HH:MM:SS"
            {
                // NB : do not use 'java.sql.Time.valueOf(s)' because the validity is not checked !
                java.util.Date ud = parseTime( sFieldName, sTrim );
                return new java.sql.Time( ud.getTime() );
            }
            else
            {
                throwException("field '" + sFieldName + "' (sql.Time) : invalid value '" + sTrim + "' ");
            }
        }
        return null;
    }
    
    /**
     * Returns a java.sql.Timestamp instance for the given date-time in ISO format "YYYY-MM-DD HH:MM:SS" 
     * @param sFieldName
     * @param sValue
     * @return the instance or null if the given value is void 
     * @since 1.0.2
     */
    protected java.sql.Timestamp getSqlTimestamp ( String sFieldName, String sValue )
    {
        if ( sValue != null )
        {
            String sTrim = sValue.trim();
            //--- Use the LENGTH to determine the type 
            int length = sTrim.length() ;
            if ( length == 0 ) // ""
            {
                return null ;
            }
            else if ( length == 19 ) // "YYYY-MM-DD HH:MM:SS"
            {
                // NB : do not use 'java.sql.Timestamp.valueOf(s)' because the validity is not checked !
                java.util.Date ud = parseDateTime( sFieldName, sTrim );
                return new java.sql.Timestamp( ud.getTime() );
            }
            else
            {
                throwException("field '" + sFieldName + "' (sql.Timestamp) : invalid value '" + sTrim + "' ");
            }
        }
        return null;
    }
    
    //-------------------------------------------------------------------------------- 
    /**
     * Returns a Boolean instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Boolean getBooleanObject(String sFieldName, String sValue)
    {
        //return ( isNullOrVoid( sValue ) ? null : new Boolean(getBoolean(sFieldName, sValue) ) ) ;
        return ( isNullOrVoid( sValue ) ? null : Boolean.valueOf(getBoolean(sFieldName, sValue) ) ) ;
    }
    
    /**
     * Returns a Character instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     * @since v 1.0.2
     */
    protected Character getCharObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Character(getChar(sFieldName, sValue) ) ) ;
    }
    
    /**
     * Returns a Byte instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Byte getByteObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Byte(getByte(sFieldName, sValue) ) ) ;
    }
    /**
     * Returns a Short instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Short getShortObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Short(getShort(sFieldName, sValue) ) ) ;
    }
    /**
     * Returns an Integer instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Integer getIntObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Integer(getInt(sFieldName, sValue) ) ) ;
    }
    /**
     * Returns a Long instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Long getLongObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Long(getLong(sFieldName, sValue) ) ) ;
    }
    /**
     * Returns a Float instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Float getFloatObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Float(getFloat(sFieldName, sValue) ) ) ;
    }
    
    /**
     * Returns a Double instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected Double getDoubleObject(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new Double(getDouble(sFieldName, sValue) ) ) ;
    }
    
    /**
     * Returns a BigDecimal instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return the object or null if the value is null or void 
     */
    protected BigDecimal getBigDecimal(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new BigDecimal(sValue.trim()) ) ;
    }
    
    /**
     * Returns a BigInteger instance built from the given string value
     * @param sFieldName
     * @param sValue
     * @return
     * @since v 1.0.2
     */
    protected BigInteger getBigInteger(String sFieldName, String sValue)
    {
        return ( isNullOrVoid( sValue ) ? null : new BigInteger(sValue.trim()) ) ;
    }
    
    //-----------------------------------------------------------------------------------
    // BEAN TO XML
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string (with XML entities) for the given string value
     * 
     * @param sValue
     * @return
     */
    protected String attributeString(String sValue)
    {
        return XmlUtil.xmlString(sValue);
    }

    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given date value
     * 
     * @param date
     * @return the date in ISO format ( YYYY-MM-DD ) or "" if date is null
     */
    protected String attributeString(java.util.Date date)
    {
        return attributeString(date, DATE_ONLY);
    }
    
    /**
     * Returns the XML attribute string for the given date value
     * @param date
     * @param iType : DATE_ONLY ( YYYY-MM-DD ), TIME_ONLY ( HH:MM:SS ) or DATE_AND_TIME ( YYYY-MM-DD HH:MM:SS )
     * @return
     */
    protected String attributeString(java.util.Date date, int iType)
    {
        if (date == null)
        {
            return "";
        }
        if ( iType == DATE_AND_TIME )
        {
            //return datetimeFormatISO.format(date); // ver 1.0.3
            return DateUtil.dateTimeISO(date); // ver 1.0.3
        }
        else if ( iType == TIME_ONLY )
        {
            //return timeFormatISO.format(date); // ver 1.0.3
            return DateUtil.timeISO(date); // ver 1.0.3
        }
        else // default DATE_ONLY
        {
            //return dateFormatISO.format(date); // ver 1.0.3
            return DateUtil.dateISO(date); // ver 1.0.3
        }
    }
    
    /**
     * Returns the XML attribute string for the given SQL date value : "YYYY-MM-DD"
     * @param d
     * @return
     * @since 1.0.2
     */
    protected String attributeString( java.sql.Date d )
    {
        //return d.toString(); // "YYYY-MM-DD"
        return ( d != null ? d.toString() : ""); // "YYYY-MM-DD" or void for NULL value // v 1.1.2
    }
    
    /**
     * Returns the XML attribute string for the given SQL time value : "HH:MM:SS"
     * @param t
     * @return
     * @since 1.0.2
     */
    protected String attributeString( java.sql.Time t )
    {
        //return t.toString(); // "HH:MM:SS"
        return ( t != null ? t.toString() : ""); // "HH:MM:SS" or void for NULL value // v 1.1.2
    }
    
    /**
     * Returns the XML attribute string for the given SQL time value : "YYYY-MM-DD HH:MM:SS"
     * @param ts
     * @return
     * @since 1.0.2
     */
    protected String attributeString( java.sql.Timestamp ts )
    {
        // return datetimeFormatISO.format(ts) ; // ver 1.0.3
        return DateUtil.dateTimeISO(ts); // ver 1.0.3
    }
    
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given boolean value
     * 
     * @param bValue
     * @return "1" for true, "0" for false
     */
    protected String attributeString(boolean bValue)
    {
        return (bValue ? "1" : "0");
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Boolean instance
     * @param v
     * @return boolean value ( "1" for true, "0" for false ) or "" if null
     */
    protected String attributeString( Boolean v)
    {
        return ( v != null ? attributeString(v.booleanValue()) : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Byte instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Byte v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Short instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Short v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Integer instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Integer v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Long instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Long v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Float instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Float v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Double instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( Double v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given BigDecimal instance
     * @param v
     * @return value or "" if null
     */
    protected String attributeString( BigDecimal v)
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given BigInteger instance
     * @param v
     * @return value or "" if null
     * @since 1.0.2
     */
    protected String attributeString( BigInteger v) // v 1.0.2
    {
        return ( v != null ? v.toString() : ""); // void for NULL value
    }
    //-----------------------------------------------------------------------------------
    /**
     * Returns the XML attribute string for the given Character instance
     * @param v
     * @return value or "" if null
     * @since 1.0.2
     */
    protected String attributeString( Character v) // v 1.0.2
    {
        return ( v != null ? XmlUtil.xmlString(v.toString()) : ""); // void for NULL value
    }

    //-----------------------------------------------------------------------------------
    /**
     * Generates a "long text" tag for the given tag content.<br>
     * Output : &lt;tagname&gt; &lt;![CDATA[...tag...content...]]&gt; &lt;/tagname&gt;
     * @param out
     * @param sTagName
     * @param sTagContent
     */
    protected void generateTextTag(PrintWriter out, String sTagName, String sTagContent )
    {
        out.println("<"+sTagName+"><![CDATA[" + sTagContent + "]]></"+sTagName+">");
    }
}